package org.univie.mappr;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Vector;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnClickListener;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ZoomButtonsController;
import android.widget.ZoomControls;
import android.widget.ZoomButtonsController.OnZoomListener;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

/**
 * The MapprView shows a Google Maps view on the screen and displays the current
 * location of the device. It also shows clickable Flickr photos.
 */
public class MapprView extends MapActivity implements LocationListener,
		OnGestureListener {

	// debugging
	private static final String TAG = "org.univie.mappr.MapprView";
	private static final int ZOOM_DEFAULT = 18;

	// general class members
	private MyLocationOverlay mLocationOverlay;
	private MapView mMapView;
	private MapController mMapController;
	private List<Overlay> mMapOverlays;
	private boolean mProgressD = false;
	private SharedPreferences sSharedPreferences;
	private boolean mIsLocationBased = false;
	private GestureDetector gestureListener;
	private final long mDelayTime = 500L;
	private static boolean sIsFree = false;
	public static boolean sIsblocked = false;
	private int mDone=0;
	private int mpsize = 0;
	private Toast mctoast;

	private AsyncTask<Void, Void, Void> mSetIsFree = new mSetIsFreeTastk();
	private ZoomButtonsController mZoomController;
	private ZoomControls mZoom;
	private Button mHomeButton;
	
	// location members
	private GeoPoint mPosition;
	private LocationManager mLocationManager;
	private String mProvider;
	private static TelephonyManager sTelephonyManager;
	private boolean mFollowPosition;
	// dialogs
	private static final int DIALOG_ABOUT = 0;

	// static fields
	public static FlickrPhoto sCurrentPhoto;
	public static ProgressDialog sLoadingBar;
	public static int sMaxResults = 100;



	/**
	 * Called when the activity is first created.
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		// initialize the SD card
		if (Environment.getExternalStorageState().equalsIgnoreCase(
				Environment.MEDIA_MOUNTED)) {
			File sdCard = Environment.getExternalStorageDirectory();
			try {
				PhotoCache.initialize(sdCard);
				ThumbnailCache.initialize(sdCard);
				FullscreenCache.initialize(sdCard);
			} catch (IOException e) {
				Log.e(TAG, "Could not create cache directory");
				e.printStackTrace();
			}
		} else {
			Log.e(TAG, "Could not open SD card");
			finish();
		}

		// initialize the map view
		mMapView = (MapView) findViewById(R.id.mapview);
		mMapView.setBuiltInZoomControls(true);

		// assign the overlays
		mMapOverlays = mMapView.getOverlays();

		// attach a controller and set to a decent zoom level
		mMapController = mMapView.getController();
		mMapController.setZoom(ZOOM_DEFAULT);

		// follow my position
		mFollowPosition = true;

		// zoom listener
		mZoomController = mMapView.getZoomButtonsController();
		mZoom = (ZoomControls) mZoomController.getZoomControls();
		mZoomController.setOnZoomListener(new OnZoomListener() {
			public void onVisibilityChanged(boolean visible) {
			}

			public void onZoom(boolean zoomIn) {
				if (zoomIn) {
					mMapController.zoomIn();

				} else {
					mMapController.zoomOut();
				}
				sIsFree = false;
				endOfChange();
			}
		});

		// gesture listener ...
		gestureListener = new GestureDetector(this);

		// getting first photos
		if (!mIsLocationBased) {
			new mSetIsFreeTastk().execute();
		}

		// prefs
		sSharedPreferences = PreferenceManager
				.getDefaultSharedPreferences(getBaseContext());

		// get a location manager for accessing current location
		mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		Criteria criteria = new Criteria();
		mProvider = mLocationManager.getBestProvider(criteria, false);

		sTelephonyManager = (TelephonyManager) getSystemService(MapprView.TELEPHONY_SERVICE);

		// get a location marker displaying the current location
		mLocationOverlay = new MyLocationOverlay(this, mMapView);
		mLocationOverlay.enableMyLocation();
		mMapView.getOverlays().add(mLocationOverlay);

		// initialise and set the ProgressDialog
		sLoadingBar = new ProgressDialog(mMapView.getContext());
		sLoadingBar.setCancelable(false);
		sLoadingBar.setIndeterminate(false);
		sLoadingBar.setButton(ProgressDialog.BUTTON1, "Hide", mHideButtonClickListener);
	//	mctoast= new Toast(mMapView.getContext());
	//	mctoast.setDuration(Toast.LENGTH_SHORT);

		// initialize the home button
		mHomeButton = (Button) findViewById(R.id.home_position);
		mHomeButton.setOnClickListener(mHomeButtonClickListener);
	}

	/**
	 * Creates the options menu
	 */
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.mappr_view_menu, menu);
		return true;
	}

	/**
	 * Handles the different menu selections
	 */
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.mappr_view_menu_item_preferences:
			Intent prefIntent = new Intent();
			prefIntent.setClass(getApplicationContext(), PreferencesView.class);
			startActivity(prefIntent);
			return true;
		case R.id.mappr_view_menu_item_about:
			showDialog(DIALOG_ABOUT);
			return true;
		case R.id.mappr_view_menu_exit:
			//should turn off gps
		/*	if(mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
			    Intent myIntent = new Intent( Settings.ACTION_SECURITY_SETTINGS );
			    startActivity(myIntent);
			}*/
			// and exit the app
			System.exit(0);
			return true;
		case R.id.mappr_view_menu_clear:
			
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("Do you really want to clear the Cache? \nsize in kb:"+String.valueOf((ThumbnailCache.get_size()+PhotoCache.get_size()+FullscreenCache.get_size())/1000)).setPositiveButton("Yes", dialogClickListener)
			.setNegativeButton("No", dialogClickListener).show();
			
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * Creates the application dialogues
	 */
	protected Dialog onCreateDialog(int id) {
		Dialog dialog = null;
		switch (id) {
		case DIALOG_ABOUT:
			AlertDialog.Builder builderAbout = new AlertDialog.Builder(this);
			builderAbout.setTitle(R.string.dialog_about_caption).setMessage(
					R.string.dialog_about_body).setCancelable(false)
					.setPositiveButton("OK",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int id) {
									dialog.dismiss();
								}
							});
			dialog = (AlertDialog) builderAbout.create();
			break;
		}
		return dialog;
	}
	// Dialog listener for yes / no dialog - clean cache
	DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
	    @Override
	    public void onClick(DialogInterface dialog, int which) {
	        switch (which){
	        case DialogInterface.BUTTON_POSITIVE:
	            //clear cache
	        	Log.d(TAG, "Starting cleanup ...");
	        	FullscreenCache.cleanup();
	        	PhotoCache.cleanup();
	        	ThumbnailCache.cleanup();
	        	
	            break;

	        case DialogInterface.BUTTON_NEGATIVE:
	            //just dismiss

	            break;
	        }
	    }
	};

	
	@Override
	public void onResume() {
		super.onResume();
		Log.i(TAG, "Activity resumed.");
		Log.d(TAG, "Overlays: " + String.valueOf(mMapOverlays.size()));

		// TODO: let him measure so you can go back to your current position, if
		// you want to
		// maybe change that with if(mIsLocationBased){} true == location based
		mLocationManager.requestLocationUpdates(mProvider, 20000, 1, this);

		// TODO: whatever for taking care of window

		sMaxResults = Integer.parseInt(sSharedPreferences.getString(
				"preferences_categories_maps_items", "0"));
		mIsLocationBased = sSharedPreferences.getBoolean(
				"preferences_categories_maps_Islocation", false);
		mProgressD = sSharedPreferences.getBoolean("preferences_categories_maps_ShowProgress", false);
		Log.d(TAG, "Location based? " + String.valueOf(mIsLocationBased));
		Log.d(TAG, "sMaxResults= " + String.valueOf(sMaxResults));

	}

	@Override
	public void onPause() {
		super.onPause();
		Log.i(TAG, "Activity paused.");
		mLocationManager.removeUpdates(this);
	}
	
	
	// override "BACK" button for long click
	@Override
	public boolean onKeyLongPress(int keyCode, KeyEvent event) {
	    if ((keyCode == KeyEvent.KEYCODE_BACK)) {
	    	//should turn off gps
		/*	if(mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
			    Intent myIntent = new Intent( Settings.ACTION_SECURITY_SETTINGS );
			    startActivity(myIntent);
			}*/
			// and end the app
	        System.exit(0);
	        return true;
	    }
	    return super.onKeyDown(keyCode, event);
	}
	
	/**
	 * Called when Hide button gets clicked
	 */
	private OnClickListener mHideButtonClickListener = new OnClickListener() {
		@Override
		public void onClick(DialogInterface dialog, int which) {
			mProgressD = false;
			sLoadingBar.dismiss();
		}
	};
	
	private View.OnClickListener mHomeButtonClickListener = new View.OnClickListener() {
		public void onClick(View v) {
			setToCenter();
		}
	};

	/**
	 * Called when the current location is changed
	 */
	public void onLocationChanged(Location location) {

		Log.d(TAG, "Location changed to: " + location.toString());

		mPosition = getGeoPointForLocation(location);

		if (mIsLocationBased) {
			new UpdateContextTask().execute(mPosition);
			setToCenter();
		} else {
			// TODO: nothing?
		}
	}

	@Override
	protected boolean isRouteDisplayed() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub

	}

	public class UpdateContextTask extends
	AsyncTask<GeoPoint, PhotoOverlay, Vector<PhotoOverlay>> {

		@Override
		protected Vector<PhotoOverlay> doInBackground(GeoPoint... location) {
		
			Log.d(TAG, "UpdateContextTask called.");
		
			// TODO update context
			Vector<PhotoOverlay> overlays = new Vector<PhotoOverlay>();
			// TODO looking if thats so working
			// getLatitudeSpan() is from left up corner to left down corner so
			// we try to get photos of the current screen
			// its not important if we use our location or the window center
			
			
			Vector<String> ids =FlickrClient.getPhotoIdsForLocation(
					location[0], mMapView.getLatitudeSpan(), mMapView
							.getLongitudeSpan());
			
			mpsize = ids.size();
			Log.d(TAG, "UpdateContextTask retrieved " + ids.size()
					+ " photos.");
			Log.d(TAG, "Max Photos possible:" + String.valueOf(sMaxResults));
		
			for (String photoid : ids) {
				// create a new overlay class with that thumbnail
				FlickrPhoto photo = FlickrClient.getPhotoForId(photoid);
				
				PhotoOverlay pOverlay = new PhotoOverlay(photo
						.getThumbnailLocal(), mMapView.getContext());
		
				// add one single item representing that photo
				OverlayItem overlayItem = new OverlayItem(photo.getLocation(),
						photo.getTitle(), photo.getId());
				pOverlay.addOverlay(overlayItem);
		
				// add the single overlay to all the other overlays
				overlays.add(pOverlay);
				publishProgress(pOverlay);
			}
			return overlays;
		}
		
		protected void onPreExecute() {
			// show ProgressBar and so on
			//	mProgressD = true;
			// isblocked is false, so you it will not be loaded until the progress is done
			sIsblocked = true;
			if(!mProgressD){
			mctoast= Toast.makeText(
						mMapView.getContext(),
						"Loading Thumbnails for location ...", Toast.LENGTH_SHORT);
			mctoast.show();
			}
			else{
			//	sLoadingBar.setProgress(0);
			//	sLoadingBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				sLoadingBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
				sLoadingBar.setMessage("Getting Photos for location ...");
				sLoadingBar.show();
			}
		}
		
		protected void onProgressUpdate(PhotoOverlay... pOverlay) {
			// add the overlay to the map overlays
			Log.d(TAG, "adding photo");
			if(!mProgressD){
				try{
					mctoast.cancel();
				}
				catch(Exception e){
					Log.e(TAG, "mctoast is canceled without beeing shown");
				}
				mctoast= Toast.makeText(
						mMapView.getContext(),
						++mDone +" Thumbnails of "+mpsize+" Loaded", Toast.LENGTH_SHORT);
				mctoast.show();
			
			}
			else{
			/*	if(mDone == 0){
					sLoadingBar.dismiss();
					sLoadingBar.setMessage("Loading Photos ...");
					sLoadingBar.setProgress(0);
					sLoadingBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
					sLoadingBar.setMax(mpsize);
					sLoadingBar.show();
				}*/
					sLoadingBar.setMessage("loading Photos");
					sLoadingBar.setMax(mpsize);
					sLoadingBar.setProgress(++mDone);
			}
			mMapOverlays.add(pOverlay[0]);
		}
		
		protected void onPostExecute(Vector<PhotoOverlay> overlays) {
			// closing the progress Bar ... and setting the ProgressStatus back
			// to 0
			Log.d(TAG, "in onPostExecute");
			// set is blocked back, so updates can be done again
			sIsblocked = false;
			sLoadingBar.setProgress(mDone = 0);
			sLoadingBar.setMax(mpsize = 100);
			sLoadingBar.setMessage("Getting Photos for location ...");
			if (!mProgressD) {
				Toast.makeText(
						mMapView.getContext(),
						"All (" + String.valueOf(overlays.size())
								+ ") Thumbnails Loaded", Toast.LENGTH_SHORT)
						.show();
			} else {
				sLoadingBar.dismiss();
				mProgressD = true;
			}
		//	mProgressD = false;
		}
	}

	/**
	 * Calculates a {@link GeoPoint} from a given {@link Location} and returns
	 * it.
	 * 
	 * @param location
	 *            The location to be recalculated
	 * @return A GeoPoint containing the location's latitude and longitude
	 */
	public GeoPoint getGeoPointForLocation(Location location) {
		double lat = location.getLatitude();
		double lng = location.getLongitude();
		return new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
	}

	// /gesture listener TODO: maybe in other file

	@Override
	public boolean onTouchEvent(MotionEvent me) {
		super.onTouchEvent(me);
		return gestureListener.onTouchEvent(me);

	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		gestureListener.onTouchEvent(ev);
		super.dispatchTouchEvent(ev);
		return true;
	}

	@Override
	public boolean onDown(MotionEvent e) {
		// Auto-generated method stub
		return false;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {

		return false;
	}

	@Override
	public void onLongPress(MotionEvent e) {
	//	setToCenter();
		Log.d(TAG, "LongPressed ...");
		sIsFree = false;
		endOfChange();
	}

	public void setToCenter() {
		// mMapController.setCenter(mPosition);
		mMapController.animateTo(mPosition);
	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {

		if (distanceX < -20 || distanceX > 20 || distanceY < -20
				|| distanceY > 20) {
			// scroll left
			if (!mIsLocationBased) {
				Log.d(TAG, "scrolled ...");
				sIsFree = false;
				endOfChange();
				return true;
			}
		}
		return false;
	}

	// this is for getting photos of screen middle
	private class mSetIsFreeTastk extends AsyncTask<Void, Void, Void> {
		@Override
		protected Void doInBackground(Void... params) {
			try {
				// just sleep the delay
				Thread.sleep(mDelayTime);
				// then set Free
				MapprView.sIsFree = true;
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				Log.d(TAG, "Error while sleeping in SetIsFree thread: "
						+ e.toString());
			}
			return null;
		}

		protected void onPostExecute(Void param) {
			Log.d(TAG, "in sIsFree == true of endofchange before getting MapCenter");
			//sLoadingBar.show();
			new UpdateContextTask().execute(mMapView.getMapCenter());
			sIsFree = false;
		}
	};

	// handling the events after some time to get photos
	public void endOfChange() {
		// if we are loading currently thumbnails it is blocked, and nothing happens
		if(!sIsblocked){
			Log.d(TAG, "inendofchange next: mSetIsFree.cancel");
			boolean canceled = mSetIsFree.cancel(true);
			Log.d(TAG, "mSetIsFree was canceld? == " + String.valueOf(canceled));
			mSetIsFree = new mSetIsFreeTastk().execute();
		}
		
	}

	@Override
	public void onShowPress(MotionEvent e) {
		// Auto-generated method stub

	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		// Auto-generated method stub
		return false;
	}

	/*	public class UpdateContextTask extends
	AsyncTask<GeoPoint, PhotoOverlay, Vector<PhotoOverlay>> {

	@Override
	protected Vector<PhotoOverlay> doInBackground(GeoPoint... location) {
	
		Log.d(TAG, "UpdateContextTask called.");
	
		// TODO update context
		Vector<PhotoOverlay> overlays = new Vector<PhotoOverlay>();
		// TODO looking if thats so working
		// getLatitudeSpan() is from left up corner to left down corner so
		// we try to get photos of the current screen
		// its not important if we use our location or the window center
		Vector<FlickrPhoto> result = FlickrClient.getPhotosForLocation(
				location[0], mMapView.getLatitudeSpan(), mMapView
						.getLongitudeSpan());
	
		Log.d(TAG, "UpdateContextTask retrieved " + result.size()
				+ " photos.");
		Log.d(TAG, "Max Photos possible:" + String.valueOf(sMaxResults));
	
		for (FlickrPhoto photo : result) {
			// create a new overlay class with that thumbnail
			PhotoOverlay pOverlay = new PhotoOverlay(photo
					.getThumbnailLocal(), mMapView.getContext());
	
			// add one single item representing that photo
			OverlayItem overlayItem = new OverlayItem(photo.getLocation(),
					photo.getTitle(), photo.getId());
			pOverlay.addOverlay(overlayItem);
	
			// add the single overlay to all the other overlays
			overlays.add(pOverlay);
			publishProgress(pOverlay);
		}
		return overlays;
	}
	
		protected void onPreExecute() {
			// show ProgressBar and so on
			//	mProgressD = true;
			
			if(!mProgressD){
				Toast.makeText(
						mMapView.getContext(),
						"Start to load thumbnails ... ", 
						Toast.LENGTH_SHORT)
						.show();
			}
			else{
				sLoadingBar.setProgress(0);
				sLoadingBar.show();
			}
		}
		
		protected void onProgressUpdate(PhotoOverlay... pOverlay) {
			// add the overlay to the map overlays
			Log.d(TAG, "adding photo");
			mMapOverlays.add(pOverlay[0]);
		}
		
		protected void onPostExecute(Vector<PhotoOverlay> overlays) {
			// closing the progress Bar ... and setting the ProgressStatus back
			// to 0
			Log.d(TAG, "in onPostExecute");
		
			if (!mProgressD) {
				Toast.makeText(
						mMapView.getContext(),
						"All (" + String.valueOf(overlays.size())
								+ ") Thumbnails Loaded", Toast.LENGTH_SHORT)
						.show();
			} else {
				sLoadingBar.dismiss();
				mProgressD = true;
			}
		//	mProgressD = false;
		}		
	} */
	
}